﻿using ArtMath.Attribute;
using ArtMath.Component;
using ArtMath.Core.Data;
using ArtMath.Core.Helper;
using ArtMath.Flag;
using ArtMath.Parameter;
using ArtMath.Resource;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Windows.Forms;

namespace ArtMath.Window
{
    public partial class AddParameterWindow : Form
    {
        public enum ParameterType
        {
            RW,
            Component,
        }
        public class ComboBoxItem
        {
            public Base Object { get; }
            public Dictionary<string, Tuple<PropertyInfo, int>> Properties { get; } = new Dictionary<string, Tuple<PropertyInfo, int>>();
            public ComboBoxItem(Base obj)
            {
                Object = obj;
                HashSet<string> set = new HashSet<string>();
                Type t = Object.GetType();
                while (t != typeof(object))
                {
                    foreach (PropertyInfo item in t.GetProperties())
                    {
                        object[] attr = item.GetCustomAttributes(typeof(MultilingualCategoryAttribute), true);
                        if (attr?.Length > 0)
                        {
                            MultilingualCategoryAttribute mca = attr[0] as MultilingualCategoryAttribute;
                            if (mca.Category == Resources.Translate("Parameter") && set.Add(item.ToString()))
                            {
                                string name;
                                MultilingualDisplayNameAttribute[] mdna = Object.GetType().GetAttributes<MultilingualDisplayNameAttribute>(item.Name);
                                if (mdna?.Length > 0)
                                {
                                    name = mdna[0].DisplayName;
                                }
                                else
                                {
                                    name = item.Name;
                                }
                                if (item.PropertyType == typeof(double) || typeof(StringX).IsAssignableFrom(item.PropertyType))
                                {
                                    Properties.Add(name, Tuple.Create(item, 0));
                                }
                                else if (item.PropertyType == typeof(PointD))
                                {
                                    if (Object.Pad.CoordinateType == CoordinateSystemType.PolarCoordinate)
                                    {
                                        Properties.Add(name + ".ρ", Tuple.Create(item, 0));
                                        Properties.Add(name + ".θ", Tuple.Create(item, 1));
                                    }
                                    else
                                    {
                                        Properties.Add(name + ".X", Tuple.Create(item, 0));
                                        Properties.Add(name + ".Y", Tuple.Create(item, 1));
                                    }
                                }
                            }
                        }
                    }
                    t = t.BaseType;
                }
            }
            public override string ToString()
            {
                return Object.Name;
            }
        }
        public string ParameterName
        {
            get { return tbName.Text; }
        }
        public string Start
        {
            get { return cbNegInf.Checked ? double.NegativeInfinity.ToString() : tbStart.Text; }
        }
        public string Stop
        {
            get { return cbPosInf.Checked ? double.PositiveInfinity.ToString() : tbStop.Text; }
        }
        public string Step
        {
            get { return tbStep.Text; }
        }
        public string Value
        {
            get { return tbValue.Text; }
        }
        public ParameterUnit ParameterUnit
        {
            get
            {
                if (rbAngle.Checked)
                {
                    return ParameterUnit.Degree;
                }
                else if (rbRad.Checked)
                {
                    return ParameterUnit.Radian;
                }
                return ParameterUnit.None;
            }
        }
        public ParameterType ParamType
        {
            get { return rbBasic.Checked ? ParameterType.RW : ParameterType.Component; }
        }
        public ComboBoxItem SelectedComponent
        {
            get { return cbCompNames.SelectedItem as ComboBoxItem; }
        }
        public string SelectedParameterName
        {
            get { return cbParamNames.SelectedItem as string; }
        }
        public bool CurrentSketchpadDisplay
        {
            get { return cbShowInPad.Checked; }
        }
        public SketchPad CurrentPad { get; }
        public AddParameterWindow(SketchPad pad)
        {
            InitializeComponent();
            Flags.Ctrl = false;
            CurrentPad = pad;
            cbCompNames.Items.Clear();
            HashSet<Base> set = new HashSet<Base>();
            CurrentPad.ForEachExistSingleObject((Base obj) =>
            {
                ComboBoxItem cbi = new ComboBoxItem(obj);
                if (cbi.Properties?.Count > 0)
                {
                    cbCompNames.Items.Add(cbi);
                }
                return true;
            });
            if (cbCompNames.Items.Count > 0)
            {
                cbCompNames.SelectedIndex = 0;
            }
        }
        private void cancel_Click(object sender, EventArgs e)
        {
            DialogResult = DialogResult.Cancel;
        }
        private void cbCompNames_SelectedIndexChanged(object sender, EventArgs e)
        {
            cbParamNames.Items.Clear();
            cbParamNames.Items.AddRange((cbCompNames.SelectedItem as ComboBoxItem).Properties.Keys.ToArray());
            if (cbParamNames.Items.Count > 0)
            {
                cbParamNames.SelectedIndex = 0;
            }
        }
        private void cbNegInf_CheckedChanged(object sender, EventArgs e)
        {
            tbStart.Enabled = !cbNegInf.Checked;
        }

        private void cbPosInf_CheckedChanged(object sender, EventArgs e)
        {
            tbStop.Enabled = !cbPosInf.Checked;
        }
        private void tbValue_Validated(object sender, EventArgs e)
        {
            if (!Calculation.Calculate(Value, out double val))
            {
                return;
            }
            if (!cbNegInf.Checked)
            {
                tbStart.Text = (val - 10).ToString();
            }
            tbStep.Text = "1";
            if (!cbPosInf.Checked)
            {
                tbStop.Text = (val + 10).ToString();
            }
        }

        private void tbName_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (tbName.TextLength == 0 && e.KeyChar > 47 && e.KeyChar < 58)
            {
                e.Handled = true;
            }
        }

        private void rbBasic_CheckedChanged(object sender, EventArgs e)
        {
            gbBasic.Enabled = rbBasic.Checked;
        }

        private void rbComp_CheckedChanged(object sender, EventArgs e)
        {
            gbComp.Enabled = rbComp.Checked;
        }

        private void btnOK_Click(object sender, EventArgs e)
        {
            if (validCheck())
            {
                DialogResult = DialogResult.OK;
            }
        }
        bool validCheck()
        {
            if (tbName.TextLength == 0 || rbBasic.Checked && (tbValue.TextLength == 0 || !cbNegInf.Checked && tbStart.TextLength == 0 || tbStep.TextLength == 0 || !cbPosInf.Checked && tbStop.TextLength == 0))
            {
                MessageBox.Show(Resources.Translate("RequiredParametersForParameter"), Resources.Translate("Exclamation"), MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                return false;
            }
            if (InputFunctionWindow.MathFunctions.ContainsValue(tbName.Text))
            {
                MessageBox.Show(Resources.Translate("InvalidName"), Resources.Translate("Error"), MessageBoxButtons.OK, MessageBoxIcon.Error);
                return false;
            }
            if (tbName.Text == "π" || tbName.Text == "pi" || tbName.Text == "e")
            {
                if (MessageBox.Show(Resources.Translate("ParameterIsConst"), Resources.Translate("Ask"), MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question) != DialogResult.Yes)
                {
                    return false;
                }
            }
            if (CurrentPad.Document.CheckParamNameExist(ParameterName))
            {
                if (MessageBox.Show(string.Format(Resources.Translate("AddExistedParameter"), ParameterName), Resources.Translate("Ask"), MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question) == DialogResult.Yes)
                {
                    tbName.Text = Utility.GetNextAvailableName(ParameterName, CurrentPad.Document.CheckParamNameExist);
                }
                else
                {
                    return false;
                }
            }
            return true;
        }
        private void btnAdd_Click(object sender, EventArgs e)
        {
            if (validCheck())
            {
                BasicParameter param = null;
                switch (ParamType)
                {
                    case ParameterType.RW:
                        param = new BasicParameter(CurrentPad.Document, ParameterName, Value, Start, Step, Stop)
                        {
                            Unit = ParameterUnit,
                        };
                        break;
                    case ParameterType.Component:
                        param = new ObjectParameter(CurrentPad.Document,
                            CurrentPad.StdObjName(ParameterName, false, false), SelectedComponent.Object,
                            SelectedComponent.Properties[SelectedParameterName].Item1.Name,
                            SelectedComponent.Properties[SelectedParameterName].Item2);
                        break;
                }
                Program.WinParameters.SketchPad_ParameterCreated(CurrentPad, param);
                if (CurrentSketchpadDisplay)
                {
                    param.ShowInPads = CurrentPad.Name;
                }
                CurrentPad.Document.Modified = true;
                tbName.Clear();
            }
        }
    }
}
